
/* NBBOTF will remove the internal bitbase on the fly */
#ifdef NBBOTF
	#ifdef WDL_PROBE
		#undef WDL_PROBE
	#endif
#else
	#define WDL_PROBE
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "common.h"
#include "fileoper.h"
#include "cache.h"
#include "gtb-probe.h"
#include "Bool.h"
#include "BinomCoef.h"
#include "Index.h"

#include "sysport.h"
#include "gtb-types.h"
#include "gtb-dec.h"
#include "debug.h"

namespace Tablebases
{

 // The main initialization routines
/////////////////////////////////////////////////////////////////////////////////////////////////////

static int GTB_scheme = 4;
static bool_t TB_INITIALIZED = FALSE;
enum  Sizes {INISIZE = 4096};
static char ini_str[INISIZE];
static void sjoin(char *s, const char *tail, size_t max) {strncat(s, tail, max - strlen(s) - 1);}

// initializes tablebases
char *
tb_init (int verbosity, int decoding_sch, const char* szEndGamePath)
{
	unsigned int zi;
	char *ret_str;
	char localstr[256];

	assert(!TB_INITIALIZED);

	InitBool();
	InitBinomCoef();


	if (verbosity) {
		ini_str[0] = '\0';
		ret_str = ini_str;
	} else {
		ret_str = NULL;
	}

	// set the endgame path
	tb_SetEndGamePath(szEndGamePath);

	// possibly chess specific
	//if (!reach_was_initialized())
	//	reach_init();


	// resolve decoding scheme
	GTB_scheme = decoding_sch;
	Uncompressed = GTB_scheme == 0;
	set_decoding_scheme(GTB_scheme);

	if (verbosity) {
		sjoin (ini_str,"\nGTB initialization\n",INISIZE);
		sprintf (localstr,"  Compression  Scheme = %d\n", GTB_scheme);
		sjoin (ini_str,localstr,INISIZE);
	}

	// Zip info needs redo
	//zi = zipinfo_init();

	//TB_AVAILABILITY = zi;

	//if (verbosity) {
	//	if (0 == zi) {
	//		sjoin (ini_str,"  Compression Indexes = **FAILED**\n",INISIZE);
	//	} else {
	//		int n, bit;

	//		n = 3; bit = 1;
	//		if (zi&(1u<<bit)) 
	//			sprintf (localstr,"  Compression Indexes (%d-pc) = PASSED\n",n);
	//		else
	//			sprintf (localstr,"  Compression Indexes (%d-pc) = **FAILED**\n",n);
	//		sjoin (ini_str,localstr,INISIZE);
	//		
	//		n = 4; bit = 3;
	//		if (zi&(1u<<bit))
	//			sprintf (localstr,"  Compression Indexes (%d-pc) = PASSED\n",n);
	//		else
	//			sprintf (localstr,"  Compression Indexes (%d-pc) = **FAILED**\n",n);
	//		sjoin (ini_str,localstr,INISIZE);

	//		n = 5; bit = 5;
	//		if (zi&(1u<<bit))
	//			sprintf (localstr,"  Compression Indexes (%d-pc) = PASSED\n",n);
	//		else
	//			sprintf (localstr,"  Compression Indexes (%d-pc) = **FAILED**\n",n);
	//		sjoin (ini_str,localstr,INISIZE);
	//	}
	//	sjoin (ini_str,"\n",INISIZE);
	//}

	// eg_was_open_reset();
	Bytes_read = 0;

#ifdef THREADING
	mythread_mutex_init (&Egtb_lock);
#endif

	TB_INITIALIZED = TRUE;

	return ret_str;
}

// tidies  up the memory for each file
mySHARED void
egtb_freemem (int i)
{
	// TO-DO: needs redo for checkers
	//if (egkey[i].status == STATUS_MALLOC) {
	//	assert (egkey[i].egt_w != NULL);
	//	assert (egkey[i].egt_b != NULL);	
	//	free (egkey[i].egt_w);
	//	free (egkey[i].egt_b);	
	//	egkey[i].egt_w = NULL;
	//	egkey[i].egt_b = NULL;
	//}	
	egkey[i].status = 0;	
}


/* tidies up the memory */
static void
RAM_egtbfree (void)
{
	int i;
	for (i = 0; egkey[i].str != NULL; i++) {
		egtb_freemem (i);
	}
}


// returns whether tablebase is available
extern unsigned int
tb_availability(void)
{
	return TB_AVAILABILITY;
}

// returns whether tablebase is initialized
extern bool_t
tb_is_initialized (void)
{
	return TB_INITIALIZED;
}

// clears the tablebases
extern void
tb_done (void)
{
	assert(TB_INITIALIZED);
	RAM_egtbfree();
	zipinfo_done();

#ifdef THREADING
	mythread_mutex_destroy (&Egtb_lock);
#endif

	TB_INITIALIZED = FALSE;

	return;
}

// restarts the tablebase system
char*
tb_restart(int verbosity, int decoding_sch, const char* szEndGamePath = NULL)
{
	if (tb_is_initialized()) {
		tb_done();
	}
	return tb_init(verbosity, decoding_sch, szEndGamePath == NULL ? tb_GetEndGamePath() : szEndGamePath);
}



mySHARED bool_t
egtb_get_id (BitBoard* p, tbkey_t *id)
{
	uint32 figs = p->wm | p->wk | p->bm | p ->bk;
	int bitCount = BitCount(figs);
	if(bitCount > 8)
		return false;
	*id = bitCount - 2;
	
	// check, file is open
	if(egkey[*id].status == 0)
		return false;

	return true;
}

mySHARED void
unpackdist (dtm_t d, unsigned int *res, unsigned int *ply)
{
	*ply = (unsigned int)d >> PLYSHIFT;
	*res = d & INFOMASK;
}

static bool_t
tb_probe_wdl(uint8_t cl, BitBoard* q, bool_t probingtype, /*@out@*/ unsigned *res)
{
	tbkey_t id = -1;
	unsigned int wdl = UNKNOWN;
	BitBoard p;
	uint8 wm, wk, bm, bk, wmrank, bmrank;
	uint8 color = cl == tb_WHITE_TO_MOVE ? WHITE : BLACK;
	const int maxpieces = 8;
	const int maxpiece = 5;
	int reverse;

	p=*q;

	// calculate number of pieces
	wm = BitCount(p.wm);
	wk = BitCount(p.wk);
	bm = BitCount(p.bm);
	bk = BitCount(p.bk);

	if(wm + wk == 0)
	{
		if(color == WHITE)
			*res = LOSS;
		else
			*res = WIN;
		return true;
	}

	if(bm + bk == 0)
	{
		if(color == WHITE)
			*res = WIN;
		else
			*res = LOSS;
		return true;
	}

	if( (bm+wm+wk+bk>maxpieces) || (bm+bk>maxpiece) || (wm+wk>maxpiece))
	{
		*res = UNKNOWN;	
		return true;
	}
	
	// decide maximum rank of men
	if(wm)
		wmrank = MSB(p.wm)/4;
	if(bm)
		bmrank = (31-LSB(p.bm))/4;	
	
	// if this BitBoard is dominated by the "wrong" side, we have to
	// reverse it!
	if (( ((bm+bk-wm-wk)<<16) + ((bk-wk)<<8) + ((bmrank-wmrank)<<4) + (1-cl)) > 0)
		reverse = 1;
	
	if (reverse)
	{
		BitBoard revpos;
		// white is dominating this BitBoard, change colors
		revpos.wm = Revert(p.bm);
		revpos.wk = Revert(p.bk);
		revpos.bm = Revert(p.wm);
		revpos.bk = Revert(p.wk);
		color = color^1;
		p = revpos;
		
		reverse = bm;
		bm = wm;
		wm = reverse;
		
		reverse = bk;
		bk = wk;
		wk = reverse;
		
		reverse = bmrank;
		bmrank = wmrank;
		wmrank = reverse;
		
		reverse = 1;
	} // if


	FOLLOW_label("EGTB_PROBE")

	if (egtb_get_id(q, &id)) {
		FOLLOW_LU("got ID",id)
	} else {
		#if defined(DEBUG)
		printf("did not get id...\n");
		output_state (stm, ws, bs, wp, bp);		
		#endif
		*res = UNKNOWN;
		//unpackdist (iFORBID, res, ply);
		return FALSE;
	}

	int okcall = egtb_get_wdl(id, wm, wk, bm, bk, wmrank, bmrank, color, probingtype, &wdl);

	FOLLOW_LU("dtmok?",okcall)
	FOLLOW_DTM("wdl", wdl)

	if (okcall) {
		*res = wdl;
	} else {
		*res = UNKNOWN;
	}

	return okcall;
} 


static bool_t
tb_probe_	(uint8_t cl, 
			BitBoard* q,		 
			 bool_t probingtype,
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply)
{
	int i = 0, j = 0;
	tbkey_t id = -1;
	dtm_t dtm;
	uint8 color = cl;
	BitBoard p;
	const int maxpieces = 8;
	const int maxpiece = 5;
	uint8 wm, wk, bm, bk, wmrank, bmrank;
	int reverse;

	p=*q;

	// calculate number of pieces
	wm = BitCount(p.wm);
	wk = BitCount(p.wk);
	bm = BitCount(p.bm);
	bk = BitCount(p.bk);

	if(wm + wk == 0)
	{
		if(color == WHITE)
			*res = LOSS;
		else
			*res = WIN;
		return true;
	}

	if(bm + bk == 0)
	{
		if(color == WHITE)
			*res = WIN;
		else
			*res = LOSS;
		return true;
	}

	if( (bm+wm+wk+bk>maxpieces) || (bm+bk>maxpiece) || (wm+wk>maxpiece))
	{
		*res = UNKNOWN;	
		return true;
	}
	
	// decide maximum rank of men
	if(wm)
		wmrank = MSB(p.wm)/4;
	if(bm)
		bmrank = (31-LSB(p.bm))/4;	
	
	// if this BitBoard is dominated by the "wrong" side, we have to
	// reverse it!
	if (( ((bm+bk-wm-wk)<<16) + ((bk-wk)<<8) + ((bmrank-wmrank)<<4) + (1-cl)) > 0)
		reverse = 1;
	
	if (reverse)
	{
		BitBoard revpos;
		// white is dominating this BitBoard, change colors
		revpos.wm = Revert(p.bm);
		revpos.wk = Revert(p.bk);
		revpos.bm = Revert(p.wm);
		revpos.bk = Revert(p.wk);
		color = color^1;
		p = revpos;
		
		reverse = bm;
		bm = wm;
		wm = reverse;
		
		reverse = bk;
		bk = wk;
		wk = reverse;
		
		reverse = bmrank;
		bmrank = wmrank;
		wmrank = reverse;
		
		reverse = 1;
	} // if


	unsigned int plies;
	unsigned int inf;


	FOLLOW_label("EGTB_PROBE")

	if (egtb_get_id (q, &id)) {
		FOLLOW_LU("got ID",id)
	} else {
		#if defined(DEBUG)
		printf("did not get id...\n");
		output_state (stm, ws, bs, wp, bp);		
		#endif
		return FALSE;
	}

	int okdtm = egtb_get_dtm (id, wm, wk, bm, bk, wmrank, bmrank, color, probingtype, &dtm);

	FOLLOW_LU("dtmok?",okdtm)
	FOLLOW_DTM("dtm", dtm)

	if (okdtm) {

		//if (straight) {
			unpackdist (dtm, res, ply);
		//} else {
		//	unpackdist (inv_dtm (dtm), res, ply);
		//}	 
	} 
	
	return okdtm;
} 


/*--------------------------------------------------------------------------*/

/****************************************************************************\
|
|
|								PROBE ZONE
|
|
\****************************************************************************/

/*prototype*/
#ifdef WDL_PROBE
static bool_t
tb_probe_wdl
			(uint8_t cl, 
			BitBoard* q,		 
			 bool_t probingtype,
			 /*@out@*/ unsigned *res);
#endif

extern bool_t
tb_probe_soft
			(uint8_t cl, 
			BitBoard* q,		 
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply)
{
	return tb_probe_ (cl, q, FALSE, res, ply);
} 

extern bool_t
tb_probe_hard
			(uint8_t cl, 
			BitBoard* q,		 
			 /*@out@*/ unsigned *res, 
			 /*@out@*/ unsigned *ply)
{
	return tb_probe_ (cl, q, TRUE, res, ply);
} 

extern bool_t
tb_probe_WDL_soft
			(uint8_t cl, 
			BitBoard* q,		 
			 /*@out@*/ unsigned *res)
{
	/* probe bitbase like, assuming no en passant */
	#ifdef WDL_PROBE
	return tb_probe_wdl    (cl, q, FALSE, res);
	#else
	return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, FALSE, res, ply);
	#endif	
} 

extern bool_t
tb_probe_WDL_hard
			(uint8_t cl, 
			BitBoard* q,		 
			 /*@out@*/ unsigned *res)
{
	#ifdef WDL_PROBE
	return tb_probe_wdl    (cl, q, TRUE, res);
	#else
	return tb_probe_ (stm, epsq, inp_wSQ, inp_bSQ, inp_wPC, inp_bPC, TRUE, res, ply);
	#endif	
} 


//mySHARED void
//list_sq_flipNS (SQUARE *s)
//{
//	while (*s != NOSQUARE) {
//		*s ^= 070;
//		s++;
//	}
//}
//
//
//mySHARED dtm_t
//packdist (unsigned int inf, unsigned int ply)
//{
//	assert (inf <= INFOMASK);
//	return (dtm_t) (inf | ply << PLYSHIFT);
//}
//
//mySHARED dtm_t
//adjust_up (dtm_t dist)
//{
//	#if 0
//	static const dtm_t adding[] = {	
//		0, 1<<PLYSHIFT, 1<<PLYSHIFT, 0, 
//		0, 1<<PLYSHIFT, 1<<PLYSHIFT, 0
//	};
//	dist += adding [dist&INFOMASK];
//	return dist;
//	#else			
//	unsigned udist = (unsigned) dist;				
//	switch (udist & INFOMASK) {
//		case iWMATE:
//		case iWMATEt:
//		case iBMATE:
//		case iBMATEt:
//			udist += (1u << PLYSHIFT);
//			break;
//		default:			
//			break;
//	}
//	return (dtm_t) udist;	
//	#endif
//}
//
//
//mySHARED dtm_t
//bestx (unsigned stm, dtm_t a, dtm_t b)
//{
//	unsigned int key;	
//	static const unsigned int
//	comparison [4] [4] = {
//	 			/*draw, wmate, bmate, forbid*/
//	/* draw  */ {0, 3, 0, 0},
//	/* wmate */ {0, 1, 0, 0},
//	/* bmate */ {3, 3, 2, 0},
//	/* forbid*/ {3, 3, 3, 0}
//
//	/* 0 = selectfirst   */
//	/* 1 = selectlowest  */
//	/* 2 = selecthighest */
//	/* 3 = selectsecond  */
//	};
//
//	static const unsigned int xorkey [2] = {0, 3};	
//	dtm_t retu[4];
//	dtm_t ret = iFORBID;
//	
//	assert (stm == WH || stm == BL);
//	assert ((a & iUNKNBIT) == 0 && (b & iUNKNBIT) == 0 );
//	
//	if (a == iFORBID)
//		return b;
//	if (b == iFORBID)
//		return a;	
//
//	retu[0] = a; /* first parameter */
//	retu[1] = a; /* the lowest by default */
//	retu[2] = b; /* highest by default */
//	retu[3]	= b; /* second parameter */
//	if (b < a) {
//		retu[1] = b;
//		retu[2] = a;		
//	}
//	
//	key = comparison [a&3] [b&3] ^ xorkey[stm];
//	ret = retu [key];
//		
//	return ret;
//}


/*--------------------------------------------------------------------------*\
 |								PACKING ZONE
 *--------------------------------------------------------------------------*/




//mySHARED bool_t
//fread_entry_packed (FILE *finp, unsigned side, dtm_t *px)
//{
//	unsigned char p[SLOTSIZE];
//	bool_t ok = (SLOTSIZE == fread (p, sizeof(unsigned char), SLOTSIZE, finp));
//	if (ok) {
//		*px = dtm_unpack (side, p[0]);
//	}
//	return ok;
//}
//
//
//mySHARED bool_t
//fpark_entry_packed  (FILE *finp, unsigned side, index_t max, index_t idx)
//{
//	bool_t ok;
//	index_t i;
//	long int fseek_i;
//	index_t sz = (index_t) sizeof(unsigned char);	
//
//	assert (side == WH || side == BL);
//	assert (finp != NULL);
//	assert (idx >= 0);
//	i = ((index_t)side * max + idx) * sz;
//	fseek_i = (long int) i;
//	assert (fseek_i >= 0);
//	ok = (0 == fseek (finp, fseek_i, SEEK_SET));
//	return ok;
//}


/****************************************************************************************************/


/****************************************************************************************************/







}

